using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using Pine.Core;
using Pine.SourceParser.Tree;
using PPT=Pine.SourceParser.Tree;

namespace Pine.CodeGen
{
	public static class UnaryOperationCodeGenerator
	{
		private static IDictionary<PPT.UnaryExpressionType, MethodInfo> unaryMethods = new Dictionary<PPT.UnaryExpressionType, MethodInfo>();
		
		static UnaryOperationCodeGenerator()
		{
			unaryMethods[PPT.UnaryExpressionType.Increment] = typeof(Value).GetMethod("Increment");
			unaryMethods[PPT.UnaryExpressionType.Decrement] = typeof(Value).GetMethod("Decrement");
			unaryMethods[PPT.UnaryExpressionType.Positive] = typeof(Value).GetMethod("Positive");
			unaryMethods[PPT.UnaryExpressionType.Negative] = typeof(Value).GetMethod("Negative");
			unaryMethods[PPT.UnaryExpressionType.LogicalNegation] = typeof(Value).GetMethod("LogicalNot");
			unaryMethods[PPT.UnaryExpressionType.BinaryNegation] = typeof(Value).GetMethod("BinaryNegation");
		}
		
		public static void GenerateBytecode(Context ctx, Expression expr, LocalBuilder lb) {
			UnaryExpression uexp = (UnaryExpression) expr;
			BuildUnaryAny(ctx, uexp, lb, unaryMethods[uexp.type]);
			
			if (uexp.type == UnaryExpressionType.Increment || uexp.type == UnaryExpressionType.Decrement) {
				if (!uexp.expr.IsMutable())
					throw new Exception("Expected mutable expression(such as variable name). Instead, got " + uexp.expr.ToString());
				
				NameIdentifier ni = uexp.expr as NameIdentifier;
				ArrayAccessExpression aae = uexp.expr as ArrayAccessExpression;
				if (ni != null) {
					CodeUtilities.MakeScopeWrite(ctx, ni.name, lb);
				}
				else if (aae != null) {
					LocalBuilder array = ctx.il.DeclareLocal(typeof(Value));
					aae.left.GenerateBytecode(ctx, array);
					LocalBuilder key = ctx.il.DeclareLocal(typeof(Value));
					aae.right.GenerateBytecode(ctx, key);
					
					ctx.il.Emit(OpCodes.Ldloc, array);
					ctx.il.Emit(OpCodes.Ldloc, key);
					ctx.il.Emit(OpCodes.Ldloc, lb);
					ctx.il.Emit(OpCodes.Callvirt, typeof(Value).GetMethod("SetArrayElement", new Type[] { typeof(Value), typeof(Value) }));
				} else {
					throw new NotImplementedException("Prefix Increment/Decrement for non-NameIdentifiers not yet implemented.");
				}
			}
		}
		
		private static void BuildUnaryAny(Context ctx, UnaryExpression uexp, LocalBuilder lb, MethodInfo method) {
			uexp.expr.GenerateBytecode(ctx, lb);
			ctx.il.Emit(OpCodes.Ldloc, lb);
			ctx.il.Emit(OpCodes.Callvirt, method);
			ctx.il.Emit(OpCodes.Stloc, lb);
		}
	}
}

